/******************************************************************************
  Filename:       zcl_ota.c
  Revised:        $Date: 2014-12-08 16:48:47 -0800 (Mon, 08 Dec 2014) $
  Revision:       $Revision: 41382 $

  Description:    Zigbee Cluster Library - Over-the-Air Upgrade Cluster ( OTA )


  Copyright 2010-2014 Texas Instruments Incorporated. All rights reserved.

  IMPORTANT: Your use of this Software is limited to those specific rights
  granted under the terms of a software license agreement between the user
  who downloaded the software, his/her employer (which must be your employer)
  and Texas Instruments Incorporated (the "License").  You may not use this
  Software unless you agree to abide by the terms of the License. The License
  limits your use, and you acknowledge, that the Software may not be modified,
  copied or distributed unless embedded on a Texas Instruments microcontroller
  or used solely and exclusively in conjunction with a Texas Instruments radio
  frequency transceiver, which is integrated into your product. Other than for
  the foregoing purpose, you may not use, reproduce, copy, prepare derivative
  works of, modify, distribute, perform, display or sell this Software and/or
  its documentation for any purpose.

  YOU FURTHER ACKNOWLEDGE AND AGREE THAT THE SOFTWARE AND DOCUMENTATION ARE
  PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED,
  INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, TITLE,
  NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL
  TEXAS INSTRUMENTS OR ITS LICENSORS BE LIABLE OR OBLIGATED UNDER CONTRACT,
  NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR OTHER
  LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES
  INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE
  OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT
  OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES
  (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS.

  Should you have any questions regarding your right to use this Software,
  contact Texas Instruments Incorporated at www.TI.com.
******************************************************************************/

/******************************************************************************
 * INCLUDES
 */
#include "ZComDef.h"
#include "OSAL.h"
#include "zcl.h"
#include "zcl_general.h"
#include "zcl_ota.h"
#include "ota_common.h"
#include "cmd_ota.h"

#include "hal_lcd.h"
#include "hal_led.h"
#include "hal_ota.h"

#include "MT_OTA.h"
#include "ZDProfile.h"
#include "ZDObject.h"

#if defined ( INTER_PAN )
#include "stub_aps.h"
#endif

#if defined OTA_MMO_SIGN
#include "ota_signature.h"
#endif

/******************************************************************************
 * MACROS
 */

/******************************************************************************
 * CONSTANTS
 */
#define OTA_MAX_TRANSACTIONS        4
#define OTA_TRANSACTION_EXPIRATION  1500

#define ZCL_OTA_HDR_LEN_OFFSET      6  // Header length location in OTA upgrade image
#define ZCL_OTA_STK_VER_OFFSET      18 // Stack version location in OTA upgrade image

#define OTA_NEW_IMAGE_QUERY_RATE    30000 // ms - 5 minutes
/******************************************************************************
 * GLOBAL VARIABLES
 */

// OTA attribute variables
uint8 zclOTA_UpgradeServerID[Z_EXTADDR_LEN];
uint32 zclOTA_FileOffset = 0xFFFFFFFF;
uint32 zclOTA_CurrentFileVersion = 0xFFFFFFFF;
uint16 zclOTA_CurrentZigBeeStackVersion;
uint32 zclOTA_DownloadedFileVersion = 0xFFFFFFFF;
uint16 zclOTA_DownloadedZigBeeStackVersion = 0xFFFF;
uint8 zclOTA_ImageUpgradeStatus;
uint16 zclOTA_ManufacturerID;
uint16 zclOTA_ImageTypeID;
uint16 zclOTA_MinBlockReqDelay = 0;
uint32 zclOTA_ImageStamp;

// Other OTA variables
uint16 zclOTA_ManufacturerId;                           // Manufacturer ID
uint16 zclOTA_ImageType;                                // Image type
afAddrType_t zclOTA_serverAddr;                         // Server address
uint8 zclOTA_AppTask = 0xFF;                            // Callback Task ID
zclOTA_QueryImageRspParams_t queryResponse;             // Global variable for sent query response

// Image block command field control value
uint8 zclOTA_ImageBlockFC = OTA_BLOCK_FC_REQ_DELAY_PRESENT; // set bitmask field control value(s) for device

#if (defined OTA_SERVER) && (OTA_SERVER == TRUE)
struct
{
  uint16 srcAddr;
  uint8  srcEp;
  uint8  seqNo;
}zclOTA_ClientSeqNo;
#endif

/******************************************************************************
 * LOCAL VARIABLES
 */
// Other OTA variables

// Task ID
static uint8 zclOTA_TaskID;

static uint8 zclOTA_Permit = TRUE;

#if defined OTA_MMO_SIGN
static OTA_MmoCtrl_t zclOTA_MmoHash;
static uint8 zclOTA_DataToHash[OTA_MMO_HASH_SIZE];
static uint8 zclOTA_HashPos;
static uint8 zclOTA_SignerIEEE[Z_EXTADDR_LEN];
static uint8 zclOTA_SignatureData[OTA_SIGNATURE_LEN];
static uint8 zclOTA_Certificate[OTA_CERTIFICATE_LEN];
#endif // OTA_MMO_SIGN

// Used by the client to correlate the Upgrade End Request and received
// Default Response.
static uint8 zclOta_OtaUpgradeEndReqTransSeq;
/******************************************************************************
 * LOCAL FUNCTIONS
 */
static ZStatus_t zclOTA_HdlIncoming ( zclIncoming_t *pInMsg );
static void zclOTA_ProcessUnhandledFoundationZCLMsgs ( zclIncomingMsg_t *pMsg );
static void zclOTA_ProcessInDefaultRspCmd( zclIncomingMsg_t *pInMsg );

#if (defined OTA_SERVER) && (OTA_SERVER == TRUE)
static ZStatus_t zclOTA_SendQueryNextImageRsp ( afAddrType_t *dstAddr, uint8 seqNo, zclOTA_QueryImageRspParams_t *pParams );
static ZStatus_t zclOTA_SendImageBlockRsp ( afAddrType_t *dstAddr, uint8 seqNo, zclOTA_ImageBlockRspParams_t *pParams );
static ZStatus_t zclOTA_SendUpgradeEndRsp ( afAddrType_t *dstAddr, uint8 seqNo, zclOTA_UpgradeEndRspParams_t *pParams );
static ZStatus_t zclOTA_SendQuerySpecificFileRsp ( afAddrType_t *dstAddr, uint8 seqNo, zclOTA_QueryImageRspParams_t *pParams );

static ZStatus_t zclOTA_Srv_QueryNextImageReq ( afAddrType_t *pSrcAddr, uint8 seqNo, zclOTA_QueryNextImageReqParams_t *pParam );
static ZStatus_t zclOTA_Srv_ImageBlockReq ( afAddrType_t *pSrcAddr, uint8 seqNo, zclOTA_ImageBlockReqParams_t *pParam );
static ZStatus_t zclOTA_Srv_ImagePageReq ( afAddrType_t *pSrcAddr, uint8 seqNo, zclOTA_ImagePageReqParams_t *pParam );
static ZStatus_t zclOTA_Srv_UpgradeEndReq ( afAddrType_t *pSrcAddr, uint8 seqNo, zclOTA_UpgradeEndReqParams_t *pParam );
static ZStatus_t zclOTA_Srv_QuerySpecificFileReq ( afAddrType_t *pSrcAddr, uint8 seqNo, zclOTA_QuerySpecificFileReqParams_t *pParam );

static void zclOTA_ProcessNextImgRsp ( uint8* pMSGpkt, zclOTA_FileID_t *pFileId, afAddrType_t *pAddr, uint8 seqNo );
static void zclOTA_ProcessFileReadRsp ( uint8* pMSGpkt, zclOTA_FileID_t *pFileId, afAddrType_t *pAddr, uint8 seqNo );
static void zclOTA_ServerHandleFileSysCb ( OTA_MtMsg_t* pMSGpkt );

static ZStatus_t zclOTA_ServerHdlIncoming ( zclIncoming_t *pInMsg );

static void zclOTA_InitBlockReqDelay ( void );

static uint8 zclOTA_GetClientSeqNumber( afAddrType_t addr );
static bool zclOTA_UpdateClientSeqNumber( afAddrType_t addr, uint8 seqNo );

#endif // (defined OTA_SERVER) && (OTA_SERVER == TRUE)

/******************************************************************************
 * OTA ATTRIBUTE DEFINITIONS - Uses REAL cluster IDs
 */

/******************************************************************************
 * OTA SIMPLE DESCRIPTOR
 */
#if defined OTA_SERVER && (OTA_SERVER == TRUE)

// Cluster ID list for match descriptor of the OTA Server.
#define ZCL_OTA_MAX_INCLUSTERS       1
const cId_t zclOTA_InClusterList[ZCL_OTA_MAX_INCLUSTERS] =
{
  ZCL_CLUSTER_ID_OTA
};
#else

#define ZCL_OTA_MAX_INCLUSTERS        0
#define zclOTA_InClusterList          NULL

#endif // (defined OTA_SERVER) && (OTA_SERVER == TRUE)

#define ZCL_OTA_MAX_OUTCLUSTERS       0
#define zclOTA_OutClusterList         NULL

SimpleDescriptionFormat_t zclOTA_SimpleDesc =
{
  ZCL_OTA_ENDPOINT,                 //  int Endpoint;
  ZCL_OTA_SAMPLE_PROFILE_ID,        //  uint16 AppProfId[2];
  ZCL_OTA_SAMPLE_DEVICEID,          //  uint16 AppDeviceId[2];
  ZCL_OTA_DEVICE_VERSION,           //  int   AppDevVer:4;
  ZCL_OTA_FLAGS,                    //  int   AppFlags:4;
  ZCL_OTA_MAX_INCLUSTERS,           //  byte  AppNumInClusters;
  ( cId_t * ) zclOTA_InClusterList, //  byte *pAppInClusterList;
  ZCL_OTA_MAX_OUTCLUSTERS,          //  byte  AppNumInClusters;
  ( cId_t * ) zclOTA_OutClusterList //  byte *pAppInClusterList;
};

// Endpoint for OTA Cluster
static endPointDesc_t zclOTA_Ep =
{
  ZCL_OTA_ENDPOINT,
  0,
#ifndef ZCL_STANDALONE
  &zcl_TaskID,
#else
  &zclOTA_TaskID,
#endif
  ( SimpleDescriptionFormat_t * ) &zclOTA_SimpleDesc,
  ( afNetworkLatencyReq_t ) 0
};

/******************************************************************************
 * @fn      zclOTA_PermitOta
 *
 * @brief   Called to enable/disable OTA operation.
 *
 * @param   permit - TRUE to enable OTA, FALSE to diable OTA
 *
 * @return  none
 */
void zclOTA_PermitOta ( uint8 permit )
{
  zclOTA_Permit = permit;
}

/******************************************************************************
 * @fn      zclOTA_Register
 *
 * @brief   Called by an application to register for callback messages
 *          from the OTA.
 *
 * @param   applicationTaskId - Application Task ID
 *
 * @return  none
 */
void zclOTA_Register ( uint8 applicationTaskId )
{
  zclOTA_AppTask = applicationTaskId;
}

uint8 zclOTA_GetTaskID( void )
{
  return zclOTA_TaskID;
}

/******************************************************************************
 * @fn      zclOTA_RequestNextUpdate
 *
 * @brief   Called by an application after discovery of the OTA server
 *          to initiate the query next image of the OTA server.
 *
 * @param   srvAddr - Short address of the server
 * @param   srvEndPoint - Endpoint on the server
 *
 * @return  ZStatus_t
 */
void zclOTA_RequestNextUpdate ( uint16 srvAddr, uint8 srvEndPoint )
{
  // Record the server address
  zclOTA_serverAddr.addrMode = afAddr16Bit;
  zclOTA_serverAddr.endPoint = srvEndPoint;
  zclOTA_serverAddr.addr.shortAddr = srvAddr;

  // Set an event to query the server
  osal_set_event ( zclOTA_TaskID, ZCL_OTA_QUERY_SERVER_EVT );
}

/******************************************************************************
 * @fn      zclOTA_Init
 *
 * @brief   Call to initialize the OTA Client Task
 *
 * @param   task_id
 *
 * @return  none
 */
void zclOTA_Init ( uint8 task_id )
{
  zclOTA_TaskID = task_id;

  // Register for the cluster endpoint
  afRegister ( &zclOTA_Ep );

  // Register as a ZCL Plugin
  zcl_registerPlugin ( ZCL_CLUSTER_ID_OTA,
                       ZCL_CLUSTER_ID_OTA,
                       zclOTA_HdlIncoming );

  // Initialize attribute variables
  zclOTA_CurrentZigBeeStackVersion = OTA_STACK_VER_PRO;
  zclOTA_ImageUpgradeStatus = OTA_STATUS_NORMAL;

  // Register with the ZDO to receive Network Address Responses
  ZDO_RegisterForZDOMsg ( task_id, IEEE_addr_rsp );

  // The default upgradeServerID is FF:FF:FF:FF:FF:FF:FF:FF
  osal_memset ( zclOTA_UpgradeServerID, 0xFF, sizeof ( zclOTA_UpgradeServerID ) );

#if defined (OTA_SERVER) && (OTA_SERVER == TRUE)

  // Register with the files system

  // Initialize rate to transfer file
  zclOTA_InitBlockReqDelay();

  zclOTA_ClientSeqNo.seqNo = 0;
  zclOTA_ClientSeqNo.srcEp = 0xFF;
  zclOTA_ClientSeqNo.srcAddr = 0xFFFE;

#endif // defined (OTA_SERVER) && (OTA_SERVER == TRUE)

}

/******************************************************************************
 * @fn          zclOTA_event_loop
 *
 * @brief       Event Loop Processor for OTA Client task.
 *
 * @param       task_id - TaskId
 *              events - events
 *
 * @return      Unprocessed event bits
 */
uint16 zclOTA_event_loop ( uint8 task_id, uint16 events )
{
  afIncomingMSGPacket_t *MSGpkt;

  if ( events & SYS_EVENT_MSG )
  {
    while ( ( MSGpkt = ( afIncomingMSGPacket_t * ) osal_msg_receive ( task_id ) ) )
    {
      switch ( MSGpkt->hdr.event )
      {
#ifdef ZCL_STANDALONE
        case AF_INCOMING_MSG_CMD:
          zcl_ProcessMessageMSG ( MSGpkt );
          break;
#endif

#if (defined OTA_SERVER) && (OTA_SERVER == TRUE)
        case MT_SYS_OTA_MSG:
          zclOTA_ServerHandleFileSysCb ( ( OTA_MtMsg_t* ) MSGpkt );
          break;
#endif
        case ZCL_INCOMING_MSG:
          zclOTA_ProcessUnhandledFoundationZCLMsgs ( ( zclIncomingMsg_t* ) MSGpkt );
          break;
          
        default:
          break;
      }

      // Release the memory
      osal_msg_deallocate ( ( uint8 * ) MSGpkt );
    }

    // return unprocessed events
    return ( events ^ SYS_EVENT_MSG );
  }

#if (defined OTA_SERVER) && (OTA_SERVER == TRUE)
  if( events & ZCL_OTA_CLIENT_SEQNUM_CLEAR_EVT )
  {
    zclOTA_ClientSeqNo.srcAddr = 0xFFFE;
    zclOTA_ClientSeqNo.srcEp = 0xFF;

    return ( events ^ ZCL_OTA_CLIENT_SEQNUM_CLEAR_EVT );
  }
#endif


  // Discard unknown events
  return 0;
}


/******************************************************************************
 * @fn          zclOTA_getStatus
 *
 * @brief       Retrieves current ZCL OTA Status
 *
 * @param       none
 *
 * @return      ZCL OTA Status
 */
uint8 zclOTA_getStatus ( void )
{
  return zclOTA_ImageUpgradeStatus;
}


/******************************************************************************
 * @fn      zclOTA_HdlIncoming
 *
 * @brief   Callback from ZCL to process incoming Commands specific
 *          to this cluster library or Profile commands for attributes
 *          that aren't in the attribute list
 *
 * @param   pInMsg - pointer to the incoming message
 *
 * @return  ZStatus_t
 */
static ZStatus_t zclOTA_HdlIncoming ( zclIncoming_t *pInMsg )
{
  ZStatus_t stat = ZSuccess;

  if ( zcl_ClusterCmd ( pInMsg->hdr.fc.type ) )
  {
    // Is this a manufacturer specific command?
    if ( pInMsg->hdr.fc.manuSpecific == 0 )
    {
      // Is command for server?
      if ( zcl_ServerCmd ( pInMsg->hdr.fc.direction ) )
      {
#if (defined OTA_SERVER) && (OTA_SERVER == TRUE)
        stat = zclOTA_ServerHdlIncoming ( pInMsg );
#else
        stat = ZCL_STATUS_UNSUP_CLUSTER_COMMAND;
#endif // defined (OTA_SERVER) && (OTA_SERVER == TRUE)
      }
      else // Else command is for client
      {
        stat = ZCL_STATUS_UNSUP_CLUSTER_COMMAND;
      }
    }
    else
    {
      // We don't support any manufacturer specific command.
      stat = ZCL_STATUS_UNSUP_MANU_CLUSTER_COMMAND;
    }
  }
  else
  {
    // Handle all the normal (Read, Write...) commands -- should never get here
    stat = ZFailure;
  }

  return ( stat );
}

/******************************************************************************
 * @fn      zclOTA_ProcessZCLMsgs
 *
 * @brief   Process unhandled foundation ZCL messages for the OTA End Point.
 *
 * @param   pMsg - a Pointer to the ZCL message
 *
 * @return  none
 */
static void zclOTA_ProcessUnhandledFoundationZCLMsgs ( zclIncomingMsg_t *pMsg )
{
  switch ( pMsg->zclHdr.commandID )
  {
    case ZCL_CMD_DEFAULT_RSP:
      zclOTA_ProcessInDefaultRspCmd( pMsg );
      break;
    default :
      break;
  }

  if ( pMsg->attrCmd )
  {
    osal_mem_free( pMsg->attrCmd );
    pMsg->attrCmd = NULL;
  }
}

/******************************************************************************
 * @fn      zclOTA_ProcessInDefaultRspCmd 
 *
 * @brief   Passed along from application.
 *
 * @param   pInMsg - Pointer to Default Response Command
 *
 * @return  void
 */
static void zclOTA_ProcessInDefaultRspCmd( zclIncomingMsg_t *pInMsg )
{
  // If the OTA server issued a Default Response, most likely something bad
  // happened.
       
  zclDefaultRspCmd_t *defRspCmd = (zclDefaultRspCmd_t*)pInMsg->attrCmd;
    
  switch ( defRspCmd->statusCode )
  {
      
    case ( ZCL_STATUS_ABORT ) :
        
      switch ( zclOTA_ImageUpgradeStatus )
      {
        case ( OTA_STATUS_COMPLETE ) :
          if ( pInMsg->zclHdr.transSeqNum == zclOta_OtaUpgradeEndReqTransSeq )
          {
            // The server has issued an ABORT while we were waiting for the 
            // Upgrade End Response.
            zclOTA_ImageUpgradeStatus = OTA_STATUS_NORMAL;
            zclOta_OtaUpgradeEndReqTransSeq = 0;
          }
          break;
          
        // Handling for reception of the Default Response with status code == 
        // ABORT, in other OTA states, can be added here.
          
        default :
          break;
      }
      break;
      
    // Handling for other Defautl Response status codes and OTA states can 
    // be added here.
    default :
      break;
  }           
}

#if (defined OTA_SERVER) && (OTA_SERVER == TRUE)
/******************************************************************************
 * @fn      zclOTA_SendImageNotify
 *
 * @brief   Send an OTA Image Notify message.
 *
 * @param   dstAddr - where you want the message to go
 * @param   seqNo - input seq number, fixed by luoyiming
 * @param   pParams - message parameters
 *
 * @return  ZStatus_t
 */
ZStatus_t zclOTA_SendImageNotify ( afAddrType_t *dstAddr, uint8 seqNo,
                                   zclOTA_ImageNotifyParams_t *pParams )
{
  ZStatus_t status;
  uint8 buf[PAYLOAD_MAX_LEN_IMAGE_NOTIFY];
  uint8 *pBuf = buf;

  *pBuf++ = pParams->payloadType;
  *pBuf++ = pParams->queryJitter;
  if ( pParams->payloadType >= NOTIFY_PAYLOAD_JITTER_MFG )
  {
    *pBuf++ = LO_UINT16 ( pParams->fileId.manufacturer );
    *pBuf++ = HI_UINT16 ( pParams->fileId.manufacturer );
  }
  if ( pParams->payloadType >= NOTIFY_PAYLOAD_JITTER_MFG_TYPE )
  {
    *pBuf++ = LO_UINT16 ( pParams->fileId.type );
    *pBuf++ = HI_UINT16 ( pParams->fileId.type );
  }
  if ( pParams->payloadType == NOTIFY_PAYLOAD_JITTER_MFG_TYPE_VERS )
  {
    pBuf = osal_buffer_uint32 ( pBuf, pParams->fileId.version );
  }

  status = zcl_SendCommand ( ZCL_OTA_ENDPOINT, dstAddr, ZCL_CLUSTER_ID_OTA,
                             COMMAND_IMAGE_NOTIFY, TRUE,
                             ZCL_FRAME_SERVER_CLIENT_DIR, TRUE, 0,
                             seqNo, ( uint16 ) ( pBuf - buf ), buf );

  return status;
}

/******************************************************************************
 * @fn      zclOTA_SendQueryNextImageRsp
 *
 * @brief   Send an OTA Query Next Image Response message.
 *
 * @param   dstAddr - where you want the message to go
 * @param   seqNo - input seq number, fixed by luoyiming
 * @param   pParams - message parameters
 *
 * @return  ZStatus_t
 */
ZStatus_t zclOTA_SendQueryNextImageRsp ( afAddrType_t *dstAddr, uint8 seqNo,
                                         zclOTA_QueryImageRspParams_t *pParams )
{
  ZStatus_t status;
  uint8 buf[PAYLOAD_MAX_LEN_QUERY_NEXT_IMAGE_RSP];
  uint8 *pBuf = buf;

  *pBuf++ = pParams->status;
  if ( pParams->status == ZCL_STATUS_SUCCESS )
  {
    *pBuf++ = LO_UINT16 ( pParams->fileId.manufacturer );
    *pBuf++ = HI_UINT16 ( pParams->fileId.manufacturer );
    *pBuf++ = LO_UINT16 ( pParams->fileId.type );
    *pBuf++ = HI_UINT16 ( pParams->fileId.type );
    pBuf = osal_buffer_uint32 ( pBuf, pParams->fileId.version );
    pBuf = osal_buffer_uint32 ( pBuf, pParams->imageSize );
  }

  status = zcl_SendCommand ( ZCL_OTA_ENDPOINT, dstAddr, ZCL_CLUSTER_ID_OTA,
                             COMMAND_QUERY_NEXT_IMAGE_RSP, TRUE,
                             ZCL_FRAME_SERVER_CLIENT_DIR, TRUE, 0,
                             seqNo, ( uint16 ) ( pBuf - buf ), buf );

  return status;
}

/******************************************************************************
 * @fn      zclOTA_SendImageBlockRsp
 *
 * @brief   Send an OTA Image Block Response mesage.
 *
 * @param   dstAddr - where you want the message to go
 * @param   seqNo - input seq number, fixed by luoyiming
 * @param   pParams - message parameters
 *
 * @return  ZStatus_t
 */
ZStatus_t zclOTA_SendImageBlockRsp ( afAddrType_t *dstAddr, uint8 seqNo,
                                     zclOTA_ImageBlockRspParams_t *pParams )
{
  uint8 *buf;
  uint8 *pBuf;
  ZStatus_t status;
  uint8 len;

  if ( pParams->status == ZCL_STATUS_SUCCESS )
  {
    len = PAYLOAD_MAX_LEN_IMAGE_BLOCK_RSP + pParams->rsp.success.dataSize;
  }
  else if ( pParams->status == ZCL_STATUS_WAIT_FOR_DATA )
  {
    len = PAYLOAD_MIN_LEN_IMAGE_BLOCK_WAIT;
  }
  else
  {
    len = 1;
  }

  buf = osal_mem_alloc ( len );

  if ( buf == NULL )
  {
    return ( ZMemError );
  }

  pBuf = buf;
  *pBuf++ = pParams->status;

  if ( pParams->status == ZCL_STATUS_SUCCESS )
  {
    *pBuf++ = LO_UINT16 ( pParams->rsp.success.fileId.manufacturer );
    *pBuf++ = HI_UINT16 ( pParams->rsp.success.fileId.manufacturer );
    *pBuf++ = LO_UINT16 ( pParams->rsp.success.fileId.type );
    *pBuf++ = HI_UINT16 ( pParams->rsp.success.fileId.type );
    pBuf = osal_buffer_uint32 ( pBuf, pParams->rsp.success.fileId.version );
    pBuf = osal_buffer_uint32 ( pBuf, pParams->rsp.success.fileOffset );
    *pBuf++ = pParams->rsp.success.dataSize;
    osal_memcpy ( pBuf, pParams->rsp.success.pData, pParams->rsp.success.dataSize );
  }
  else if ( pParams->status == ZCL_STATUS_WAIT_FOR_DATA )
  {
    pBuf = osal_buffer_uint32 ( pBuf, pParams->rsp.wait.currentTime );
    pBuf = osal_buffer_uint32 ( pBuf, pParams->rsp.wait.requestTime );
    *pBuf++ = LO_UINT16 ( pParams->rsp.wait.blockReqDelay );
    *pBuf++ = HI_UINT16 ( pParams->rsp.wait.blockReqDelay );
  }

  status = zcl_SendCommand ( ZCL_OTA_ENDPOINT, dstAddr, ZCL_CLUSTER_ID_OTA,
                             COMMAND_IMAGE_BLOCK_RSP, TRUE,
                             ZCL_FRAME_SERVER_CLIENT_DIR, TRUE, 0,
                             seqNo, len, buf );

  osal_mem_free ( buf );

  return status;
}

/******************************************************************************
 * @fn      zclOTA_SendUpgradeEndRsp
 *
 * @brief   Send an OTA Upgrade End Response mesage.
 *
 * @param   dstAddr - where you want the message to go
 * @param   seqNo - input seq number, fixed by luoyiming
 * @param   pParams - message parameters
 *
 * @return  ZStatus_t
 */
ZStatus_t zclOTA_SendUpgradeEndRsp ( afAddrType_t *dstAddr, uint8 seqNo,
                                     zclOTA_UpgradeEndRspParams_t *pParams )
{
  ZStatus_t status;
  uint8 buf[PAYLOAD_MAX_LEN_UPGRADE_END_RSP];
  uint8 *pBuf = buf;

  *pBuf++ = LO_UINT16 ( pParams->fileId.manufacturer );
  *pBuf++ = HI_UINT16 ( pParams->fileId.manufacturer );
  *pBuf++ = LO_UINT16 ( pParams->fileId.type );
  *pBuf++ = HI_UINT16 ( pParams->fileId.type );
  pBuf = osal_buffer_uint32 ( pBuf, pParams->fileId.version );
  pBuf = osal_buffer_uint32 ( pBuf, pParams->currentTime );
  pBuf = osal_buffer_uint32 ( pBuf, pParams->upgradeTime );

  status = zcl_SendCommand ( ZCL_OTA_ENDPOINT, dstAddr, ZCL_CLUSTER_ID_OTA,
                             COMMAND_UPGRADE_END_RSP, TRUE,
                             ZCL_FRAME_SERVER_CLIENT_DIR, TRUE, 0,
                             seqNo, PAYLOAD_MAX_LEN_UPGRADE_END_RSP, buf );

  return status;
}

/******************************************************************************
 * @fn      zclOTA_SendQuerySpecificFileRsp
 *
 * @brief   Send an OTA Query Specific File Response mesage.
 *
 * @param   dstAddr - where you want the message to go
 * @param   seqNo - input seq number, fixed by luoyiming
 * @param   pParams - message parameters
 *
 * @return  ZStatus_t
 */
ZStatus_t zclOTA_SendQuerySpecificFileRsp ( afAddrType_t *dstAddr, uint8 seqNo,
                                            zclOTA_QueryImageRspParams_t *pParams )
{
  ZStatus_t status;
  uint8 buf[PAYLOAD_MAX_LEN_QUERY_SPECIFIC_FILE_RSP];
  uint8 *pBuf = buf;

  *pBuf++ = pParams->status;
  if ( pParams->status == ZCL_STATUS_SUCCESS )
  {
    *pBuf++ = LO_UINT16 ( pParams->fileId.manufacturer );
    *pBuf++ = HI_UINT16 ( pParams->fileId.manufacturer );
    *pBuf++ = LO_UINT16 ( pParams->fileId.type );
    *pBuf++ = HI_UINT16 ( pParams->fileId.type );
    pBuf = osal_buffer_uint32 ( pBuf, pParams->fileId.version );
    pBuf = osal_buffer_uint32 ( pBuf, pParams->imageSize );
  }

  status = zcl_SendCommand ( ZCL_OTA_ENDPOINT, dstAddr, ZCL_CLUSTER_ID_OTA,
                             COMMAND_QUERY_SPECIFIC_FILE_RSP, TRUE,
                             ZCL_FRAME_SERVER_CLIENT_DIR, TRUE, 0,
                             seqNo, ( uint16 ) ( pBuf - buf ), buf );

  return status;
}
#endif // defined (OTA_SERVER) && (OTA_SERVER == TRUE)

#if defined (OTA_SERVER) && (OTA_SERVER == TRUE)
/******************************************************************************
 * @fn      zclOTA_ProcessNextImgRsp
 *
 * @brief   Handles a response to a MT_OTA_NEXT_IMG_RSP.
 *
 * @param   pMsg - The data from the server.
 *          pFileId - The ID of the OTA File.
 *          pAddr - The source of the message.
 *
 * @return  none
 */
void zclOTA_ProcessNextImgRsp ( uint8* pMsg, zclOTA_FileID_t *pFileId,
                                afAddrType_t *pAddr, uint8 seqNo )
{
  zclOTA_QueryImageRspParams_t queryRsp;
  uint8 options;
  uint8 status;

  // Get the status of the operation
  status = *pMsg++;

  // Get the options
  options = *pMsg++;

  // Copy the file ID
  osal_memcpy ( &queryRsp.fileId, pFileId, sizeof ( zclOTA_FileID_t ) );

  // Set the image size
  if ( status == ZSuccess )
  {
    queryRsp.status = ZSuccess;
    queryRsp.imageSize = BUILD_UINT32 ( pMsg[0], pMsg[1], pMsg[2], pMsg[3] );
  }
  else
  {
    queryRsp.status = ZOtaNoImageAvailable;
    queryRsp.imageSize = 0;
  }

  queryResponse = queryRsp; // save global variable for query image response. Used later in image block request check

  // Send a response to the client
  if ( options & MT_OTA_QUERY_SPECIFIC_OPTION )
  {
    zclOTA_SendQuerySpecificFileRsp ( pAddr, seqNo, &queryRsp );
  }
  else
  {
    zclOTA_SendQueryNextImageRsp ( pAddr, seqNo, &queryRsp );
  }
}

/******************************************************************************
 * @fn      zclOTA_ProcessFileReadRsp
 *
 * @brief   Handles a response to a MT_OTA_FILE_READ_RSP.
 *
 * @param   pMsg - The data from the server.
 *          pFileId - The ID of the OTA File.
 *          pAddr - The source of the message.
 *
 * @return  none
 */
void zclOTA_ProcessFileReadRsp ( uint8* pMsg, zclOTA_FileID_t *pFileId,
                                 afAddrType_t *pAddr, uint8 seqNo )
{
  zclOTA_ImageBlockRspParams_t blockRsp;

  // Set the status
  blockRsp.status = *pMsg++;

  // Check the status of the file read
  if ( blockRsp.status == ZSuccess )
  {
    // Fill in the response parameters
    osal_memcpy ( &blockRsp.rsp.success.fileId, pFileId, sizeof ( zclOTA_FileID_t ) );
    blockRsp.rsp.success.fileOffset = BUILD_UINT32 ( pMsg[0], pMsg[1], pMsg[2], pMsg[3] );
    pMsg += 4;
    blockRsp.rsp.success.dataSize = *pMsg++;
    blockRsp.rsp.success.pData = pMsg;
  }
  else
  {
    blockRsp.status = ZOtaAbort;
  }

  // Send the block response to the peer
  zclOTA_SendImageBlockRsp ( pAddr, seqNo, &blockRsp );
}

/******************************************************************************
 * @fn      OTA_HandleFileSysCb
 *
 * @brief   Handles File Server Callbacks.
 *
 * @param   pMSGpkt - The data from the server.
 *
 * @return  none
 */
void zclOTA_ServerHandleFileSysCb ( OTA_MtMsg_t* pMSGpkt )
{
  zclOTA_FileID_t pFileId;
  afAddrType_t pAddr;
  uint8 *pMsg;
  uint8 seqNo;

  if ( pMSGpkt != NULL )
  {
    // Get the File ID and AF Address
    pMsg = pMSGpkt->data;
    pMsg = OTA_StreamToFileId ( &pFileId, pMsg );
    pMsg = OTA_StreamToAfAddr ( &pAddr, pMsg );
    seqNo = zclOTA_GetClientSeqNumber( pAddr );

    switch ( pMSGpkt->cmd )
    {
      case OTA_CMD_NEXT_IMG:  //MT_OTA_NEXT_IMG_RSP
        zclOTA_ProcessNextImgRsp ( pMsg, &pFileId, &pAddr, seqNo );
        break;

      case OTA_CMD_FILE_READ:  //MT_OTA_FILE_READ_RSP
        zclOTA_ProcessFileReadRsp ( pMsg, &pFileId, &pAddr, seqNo );
        break;

      default:
        break;
    }
  }
}

/******************************************************************************
 * @fn      zclOTA_Srv_QueryNextImageReq
 *
 * @brief   Handle a Query Next Image Request.
 *
 * @param   pSrcAddr - The source of the message
 *          pParam - message parameters
 *
 * @return  ZStatus_t
 *
 * @note    On a query next image, we must request a file listing
 *          from the File Server.  Then open a file if
 */
ZStatus_t zclOTA_Srv_QueryNextImageReq ( afAddrType_t *pSrcAddr, uint8 seqNo, zclOTA_QueryNextImageReqParams_t *pParam )
{
  uint8 options = 0;
  uint8 status;

  if ( zclOTA_Permit )
  {
    if ( pParam->fieldControl )
    {
      options |= MT_OTA_HW_VER_PRESENT_OPTION;
    }

    // Request the next image for this device from the console via the MT File System
    if( zclOTA_UpdateClientSeqNumber( *pSrcAddr, seqNo ) )
    {
      status = cmd_OtaGetImage ( pSrcAddr, &pParam->fileId, pParam->hardwareVersion, NULL, options );
    }
  }
  else
  {
    status = ZOtaNoImageAvailable;
  }

  if ( status != ZSuccess )
  {
    zclOTA_QueryImageRspParams_t queryRsp;

    // Fill in the response parameters
    osal_memcpy ( &queryRsp.fileId, &pParam->fileId, sizeof ( zclOTA_FileID_t ) );
    queryRsp.status = ZOtaNoImageAvailable;
    queryRsp.imageSize = 0;

    // Send a failure response to the client
    zclOTA_SendQueryNextImageRsp ( pSrcAddr, seqNo, &queryRsp );
  }

  return ZCL_STATUS_CMD_HAS_RSP;
}

/******************************************************************************
 * @fn      zclOTA_Srv_ImageBlockReq
 *
 * @brief   Handle an Image Block Request.
 *
 * @param   pSrcAddr - The source of the message
 *          pParam - message parameters
 *
 * @return  ZStatus_t
 */
ZStatus_t zclOTA_Srv_ImageBlockReq ( afAddrType_t *pSrcAddr, uint8 seqNo, zclOTA_ImageBlockReqParams_t *pParam )
{
  uint8 status = ZFailure;

  if ( pParam->fileId.version != queryResponse.fileId.version )
  {
    status = ZCL_STATUS_NO_IMAGE_AVAILABLE;
  }
  else
  {

    if ( zclOTA_Permit && ( pParam != NULL ) )
    {
      uint8 len = pParam->maxDataSize;

      if ( len > OTA_MAX_MTU )
      {
        len = OTA_MAX_MTU;
      }

      // The item already exists in NV memory, read it from NV memory
      osal_nv_read ( ZCD_NV_OTA_BLOCK_REQ_DELAY, 0,
                     sizeof ( zclOTA_MinBlockReqDelay ), &zclOTA_MinBlockReqDelay );

      // check if client supports rate limiting feature, and if client rate needs to be set
      if ( ( ( pParam->fieldControl & OTA_BLOCK_FC_REQ_DELAY_PRESENT ) != 0 ) &&
           ( pParam->blockReqDelay != zclOTA_MinBlockReqDelay ) )
      {
        zclOTA_ImageBlockRspParams_t blockRsp;

        // Fill in the response parameters
        blockRsp.status = ZOtaWaitForData;
        osal_memcpy ( &blockRsp.rsp.success.fileId, &pParam->fileId, sizeof ( zclOTA_FileID_t ) );
        blockRsp.rsp.wait.currentTime = 0;
        blockRsp.rsp.wait.requestTime = 0;
        blockRsp.rsp.wait.blockReqDelay = zclOTA_MinBlockReqDelay;

        // Send a wait response with updated rate limit timing
        zclOTA_SendImageBlockRsp ( pSrcAddr, seqNo, &blockRsp );
      }
      else
      {
        // Read the data from the OTA Console
        if( zclOTA_UpdateClientSeqNumber( *pSrcAddr, seqNo ) )
        {
          status = cmd_OtaFileReadReq ( pSrcAddr, &pParam->fileId, len, pParam->fileOffset );
        }

        // Send a wait response to the client
        if ( status != ZSuccess )
        {
          zclOTA_ImageBlockRspParams_t blockRsp;

          // Fill in the response parameters
          blockRsp.status = ZOtaWaitForData;
          osal_memcpy ( &blockRsp.rsp.success.fileId, &pParam->fileId, sizeof ( zclOTA_FileID_t ) );
          blockRsp.rsp.wait.currentTime = 0;
          blockRsp.rsp.wait.requestTime = OTA_SEND_BLOCK_WAIT;
          blockRsp.rsp.wait.blockReqDelay = zclOTA_MinBlockReqDelay;

          // Send the block to the peer
          zclOTA_SendImageBlockRsp ( pSrcAddr, seqNo, &blockRsp );
        }
      }

      status = ZCL_STATUS_CMD_HAS_RSP;

    }
  }

  return status;
}

/******************************************************************************
 * @fn      zclOTA_Srv_ImagePageReq
 *
 * @brief   Handle an Image Page Request.  Note: Not currently supported.
 *
 * @param   pSrcAddr - The source of the message
 *          pParam - message parameters
 *
 * @return  ZStatus_t
 */
ZStatus_t zclOTA_Srv_ImagePageReq ( afAddrType_t *pSrcAddr, uint8 seqNo, zclOTA_ImagePageReqParams_t *pParam )
{
  // Send not supported resposne
  return ZUnsupClusterCmd;
}

/******************************************************************************
 * @fn      zclOTA_Srv_UpgradeEndReq
 *
 * @brief   Handle an Upgrade End Request.
 *
 * @param   pSrcAddr - The source of the message
 *          pParam - message parameters
 *
 * @return  ZStatus_t
 */
ZStatus_t zclOTA_Srv_UpgradeEndReq ( afAddrType_t *pSrcAddr, uint8 seqNo, zclOTA_UpgradeEndReqParams_t *pParam )
{
  uint8 status = ZFailure;
  
  if ( zclOTA_Permit && ( pParam != NULL ) )
  {
    zclOTA_UpgradeEndRspParams_t rspParms;

    if ( pParam->status == ZSuccess )
    {
      osal_memcpy ( &rspParms.fileId, &pParam->fileId, sizeof ( zclOTA_FileID_t ) );
      rspParms.currentTime = osal_GetSystemClock();
      rspParms.upgradeTime = rspParms.currentTime + OTA_UPGRADE_DELAY;

      // Send the response to the peer
      zclOTA_SendUpgradeEndRsp ( pSrcAddr, seqNo, &rspParms );
    }

    // Notify the Console Tool
    cmd_OtaSendStatus ( pSrcAddr, MT_OTA_DL_COMPLETE, pParam->status, 0, &pParam->fileId );

    status = ZCL_STATUS_CMD_HAS_RSP;
  }

  return status;
}

/******************************************************************************
 * @fn      zclOTA_Srv_QuerySpecificFileReq
 *
 * @brief   Handles a Query Specific File Request.
 *
 * @param   pSrcAddr - The source of the message
 *          pParam - message parameters
 *
 * @return  ZStatus_t
 */
ZStatus_t zclOTA_Srv_QuerySpecificFileReq ( afAddrType_t *pSrcAddr, uint8 seqNo, zclOTA_QuerySpecificFileReqParams_t *pParam )
{
  uint8 status;

  // Request the image from the console
  if ( zclOTA_Permit )
  {
    status = cmd_OtaGetImage ( pSrcAddr, &pParam->fileId, 0,  pParam->nodeAddr, MT_OTA_QUERY_SPECIFIC_OPTION );
  }
  else
  {
    status = ZOtaNoImageAvailable;
  }

  if ( status != ZSuccess )
  {
    zclOTA_QueryImageRspParams_t queryRsp;

    // Fill in the response parameters
    osal_memcpy ( &queryRsp.fileId, &pParam->fileId, sizeof ( zclOTA_FileID_t ) );
    queryRsp.status = ZOtaNoImageAvailable;
    queryRsp.imageSize = 0;

    // Send a failure response to the client
    zclOTA_SendQuerySpecificFileRsp ( pSrcAddr, seqNo, &queryRsp );
  }

  return ZCL_STATUS_CMD_HAS_RSP;
}

/******************************************************************************
 * @fn      zclOTA_ProcessQueryNextImageReq
 *
 * @brief   Process received Query Next Image Request.
 *
 * @param   pInMsg - pointer to the incoming message
 *
 * @return  ZStatus_t
 */
static ZStatus_t zclOTA_ProcessQueryNextImageReq ( zclIncoming_t *pInMsg )
{
  zclOTA_QueryNextImageReqParams_t  param;
  uint8 *pData;

  /* verify message length */
  if ( ( pInMsg->pDataLen != PAYLOAD_MAX_LEN_QUERY_NEXT_IMAGE_REQ ) &&
       ( pInMsg->pDataLen != PAYLOAD_MIN_LEN_QUERY_NEXT_IMAGE_REQ ) )
  {
    /* no further processing if invalid */
    return ZCL_STATUS_MALFORMED_COMMAND;
  }

  /* parse message parameters */
  pData = pInMsg->pData;
  param.fieldControl = *pData++;
  param.fileId.manufacturer = BUILD_UINT16 ( pData[0], pData[1] );
  pData += 2;
  param.fileId.type = BUILD_UINT16 ( pData[0], pData[1] );
  pData += 2;
  param.fileId.version = osal_build_uint32 ( pData, 4 );
  pData += 4;
  if ( ( param.fieldControl & 0x01 ) != 0 )
  {
    param.hardwareVersion = BUILD_UINT16 ( pData[0], pData[1] );
  }

  /* call callback */
  return zclOTA_Srv_QueryNextImageReq ( &pInMsg->msg->srcAddr, pInMsg->hdr.transSeqNum, &param );
}

/******************************************************************************
 * @fn      zclOTA_ProcessImageBlockReq
 *
 * @brief   Process received Image Block Request.
 *
 * @param   pInMsg - pointer to the incoming message
 *
 * @return  ZStatus_t
 */
static ZStatus_t zclOTA_ProcessImageBlockReq ( zclIncoming_t *pInMsg )
{
  zclOTA_ImageBlockReqParams_t  param;
  uint8 *pData;

  /* verify message length */
  if ( ( pInMsg->pDataLen > PAYLOAD_MAX_LEN_IMAGE_BLOCK_REQ ) &&
       ( pInMsg->pDataLen < PAYLOAD_MIN_LEN_IMAGE_BLOCK_REQ ) )
  {
    /* no further processing if invalid */
    return ZCL_STATUS_MALFORMED_COMMAND;
  }

  /* parse message parameters */
  pData = pInMsg->pData;
  param.fieldControl = *pData++;
  param.fileId.manufacturer = BUILD_UINT16 ( pData[0], pData[1] );
  pData += 2;
  param.fileId.type = BUILD_UINT16 ( pData[0], pData[1] );
  pData += 2;
  param.fileId.version = osal_build_uint32 ( pData, 4 );
  pData += 4;
  param.fileOffset = osal_build_uint32 ( pData, 4 );
  pData += 4;
  param.maxDataSize = *pData++;
  if ( ( param.fieldControl & OTA_BLOCK_FC_NODES_IEEE_PRESENT ) != 0 )
  {
    osal_cpyExtAddr ( param.nodeAddr, pData );
    pData += 8;
  }
  if ( ( param.fieldControl & OTA_BLOCK_FC_REQ_DELAY_PRESENT ) != 0 )
  {
    param.blockReqDelay = BUILD_UINT16 ( pData[0], pData[1] );
  }

  /* call callback */
  return zclOTA_Srv_ImageBlockReq ( &pInMsg->msg->srcAddr, pInMsg->hdr.transSeqNum, &param );
}

/******************************************************************************
 * @fn      zclOTA_ProcessImagePageReq
 *
 * @brief   Process received Image Page Request.
 *
 * @param   pInMsg - pointer to the incoming message
 *
 * @return  ZStatus_t
 */
static ZStatus_t zclOTA_ProcessImagePageReq ( zclIncoming_t *pInMsg )
{
  zclOTA_ImagePageReqParams_t  param;
  uint8 *pData;

  /* verify message length */
  if ( ( pInMsg->pDataLen != PAYLOAD_MAX_LEN_IMAGE_PAGE_REQ ) &&
       ( pInMsg->pDataLen != PAYLOAD_MIN_LEN_IMAGE_PAGE_REQ ) )
  {
    /* no further processing if invalid */
    return ZCL_STATUS_MALFORMED_COMMAND;
  }

  /* parse message parameters */
  pData = pInMsg->pData;
  param.fieldControl = *pData++;
  param.fileId.manufacturer = BUILD_UINT16 ( pData[0], pData[1] );
  pData += 2;
  param.fileId.type = BUILD_UINT16 ( pData[0], pData[1] );
  pData += 2;
  param.fileId.version = osal_build_uint32 ( pData, 4 );
  pData += 4;
  param.fileOffset = osal_build_uint32 ( pData, 4 );
  pData += 4;
  param.maxDataSize = *pData++;
  param.pageSize = BUILD_UINT16 ( pData[0], pData[1] );
  pData += 2;
  param.responseSpacing = BUILD_UINT16 ( pData[0], pData[1] );
  pData += 2;
  if ( ( param.fieldControl & 0x01 ) != 0 )
  {
    osal_cpyExtAddr ( param.nodeAddr, pData );
  }

  /* call callback */
  return zclOTA_Srv_ImagePageReq ( &pInMsg->msg->srcAddr, pInMsg->hdr.transSeqNum, &param );
}

/******************************************************************************
 * @fn      zclOTA_ProcessUpgradeEndReq
 *
 * @brief   Process received Upgrade End Request.
 *
 * @param   pInMsg - pointer to the incoming message
 *
 * @return  ZStatus_t
 */
static ZStatus_t zclOTA_ProcessUpgradeEndReq ( zclIncoming_t *pInMsg )
{
  zclOTA_UpgradeEndReqParams_t  param;
  uint8 *pData;

  /* verify message length */
  if ( ( pInMsg->pDataLen != PAYLOAD_MAX_LEN_UPGRADE_END_REQ ) &&
       ( pInMsg->pDataLen != PAYLOAD_MIN_LEN_UPGRADE_END_REQ ) )
  {
    /* no further processing if invalid */
    return ZCL_STATUS_MALFORMED_COMMAND;
  }

  /* parse message parameters */
  pData = pInMsg->pData;
  param.status = *pData++;
  if ( param.status == ZCL_STATUS_SUCCESS )
  {
    param.fileId.manufacturer = BUILD_UINT16 ( pData[0], pData[1] );
    pData += 2;
    param.fileId.type = BUILD_UINT16 ( pData[0], pData[1] );
    pData += 2;
    param.fileId.version = osal_build_uint32 ( pData, 4 );
  }

  /* call callback */
  return zclOTA_Srv_UpgradeEndReq ( &pInMsg->msg->srcAddr, pInMsg->hdr.transSeqNum, &param );
}

/******************************************************************************
 * @fn      zclOTA_ProcessQuerySpecificFileReq
 *
 * @brief   Process received Image Page Request.
 *
 * @param   pInMsg - pointer to the incoming message
 *
 * @return  ZStatus_t
 */
static ZStatus_t zclOTA_ProcessQuerySpecificFileReq ( zclIncoming_t *pInMsg )
{
  zclOTA_QuerySpecificFileReqParams_t  param;
  uint8 *pData;

  /* verify message length */
  if ( pInMsg->pDataLen != PAYLOAD_MAX_LEN_QUERY_SPECIFIC_FILE_REQ )
  {
    /* no further processing if invalid */
    return ZCL_STATUS_MALFORMED_COMMAND;
  }

  /* parse message parameters */
  pData = pInMsg->pData;
  osal_cpyExtAddr ( param.nodeAddr, pData );
  pData += Z_EXTADDR_LEN;
  param.fileId.manufacturer = BUILD_UINT16 ( pData[0], pData[1] );
  pData += 2;
  param.fileId.type = BUILD_UINT16 ( pData[0], pData[1] );
  pData += 2;
  param.fileId.version = osal_build_uint32 ( pData, 4 );
  pData += 4;
  param.stackVersion = BUILD_UINT16 ( pData[0], pData[1] );

  /* call callback */
  return zclOTA_Srv_QuerySpecificFileReq ( &pInMsg->msg->srcAddr, pInMsg->hdr.transSeqNum, &param );
}

/******************************************************************************
 * @fn      zclOTA_ServerHdlIncoming
 *
 * @brief   Handle incoming server commands.
 *
 * @param   pInMsg - pointer to the incoming message
 *
 * @return  ZStatus_t
 */
static ZStatus_t zclOTA_ServerHdlIncoming ( zclIncoming_t *pInMsg )
{
  switch ( pInMsg->hdr.commandID )
  {
    case COMMAND_QUERY_NEXT_IMAGE_REQ:
      return zclOTA_ProcessQueryNextImageReq ( pInMsg );

    case COMMAND_IMAGE_BLOCK_REQ:
      return zclOTA_ProcessImageBlockReq ( pInMsg );

    case COMMAND_IMAGE_PAGE_REQ:
      return zclOTA_ProcessImagePageReq ( pInMsg );

    case COMMAND_UPGRADE_END_REQ:
      return zclOTA_ProcessUpgradeEndReq ( pInMsg );

    case COMMAND_QUERY_SPECIFIC_FILE_REQ:
      return zclOTA_ProcessQuerySpecificFileReq ( pInMsg );

    default:
      return ZFailure;
  }
}

/*********************************************************************
 * @fn          zclOTA_InitBlockReqDelay
 *
 * @brief       Initialization attribute Minimum Block Request Delay.
 *
 * @param       none
 *
 * @return      none
 */
static void zclOTA_InitBlockReqDelay ( void )
{
  // If the item doesn't exist in NV memory, create and initialize
  // it with the value passed in.
  if ( osal_nv_item_init ( ZCD_NV_OTA_BLOCK_REQ_DELAY,
                           sizeof ( zclOTA_MinBlockReqDelay ),
                           &zclOTA_MinBlockReqDelay ) == ZSuccess )
  {
    // The item already exists in NV memory, read it from NV memory
    osal_nv_read ( ZCD_NV_OTA_BLOCK_REQ_DELAY, 0,
                   sizeof ( zclOTA_MinBlockReqDelay ), &zclOTA_MinBlockReqDelay );
  }
}

/******************************************************************************
 * @fn      zclOTA_GetClientSeqNumber
 *
 * @brief   get sequence number for response.
 *
 * @param   addr - cilent's address.
 *
 * @return  sequence number
 */
static uint8 zclOTA_GetClientSeqNumber( afAddrType_t addr )
{
  if( (zclOTA_ClientSeqNo.srcAddr == addr.addr.shortAddr) &&
     (zclOTA_ClientSeqNo.srcEp == addr.endPoint) )
  {
    zclOTA_ClientSeqNo.srcAddr = 0xFFFE;
    zclOTA_ClientSeqNo.srcEp = 0xFF;
    return zclOTA_ClientSeqNo.seqNo;
  }
  return 0;
}

/******************************************************************************
 * @fn      zclOTA_UpdateClientSeqNumber
 *
 * @brief   update sequence number for request.
 *
 * @param   addr - cilent's address.
 *          seqNo - sequence number
 *
 * @return  none
 */
static bool zclOTA_UpdateClientSeqNumber( afAddrType_t addr, uint8 seqNo )
{
  if ( ( zclOTA_ClientSeqNo.srcAddr == 0xFFFE ) && 
       ( zclOTA_ClientSeqNo.srcEp == 0xFF ) )
  {
    osal_start_timerEx( zclOTA_TaskID, ZCL_OTA_CLIENT_SEQNUM_CLEAR_EVT, OTA_CLIENT_SEQNUM_TIMEOUT );
    zclOTA_ClientSeqNo.srcAddr = addr.addr.shortAddr;
    zclOTA_ClientSeqNo.srcEp = addr.endPoint;
    zclOTA_ClientSeqNo.seqNo = seqNo;
    return TRUE;
  }
  return FALSE;
}

/******************************************************************************
 * @fn      zclOTA_SeqNumEntryFree
 *
 * @brief   Check if SeqNumEntryTable is not full.
 *
 * @param   shortAddr - Short address
 *
 * @return  TRUE if not full
 */
bool zclOTA_SeqNumEntryFree( uint16 shortAddr )
{
  if((zclOTA_ClientSeqNo.srcAddr == 0xFFFF ) ||
     (zclOTA_ClientSeqNo.srcAddr == shortAddr))
  {
    return TRUE;
  }
  return FALSE;
}

#endif // defined (OTA_SERVER) && (OTA_SERVER == TRUE)


